﻿using System;
using System.Linq;
using System.Linq.Expressions;

//MO RONG CAC HAM CUA LINQ
//TUESDAY, JAN 25, 2011 - MANH.NGUYEN

namespace Project.MVC.Web.Helper.jqGrid
{

    public static class LinqExtensions
    {
        /// <summary>Orders the sequence by specific column and direction.</summary>
        /// <param name="query">The query.</param>
        /// <param name="sortColumn">The sort column.</param>
        /// <param name="direction">if set to true [ascending].</param>
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string sortColumn, string direction)
        {
            string methodName = string.Format("OrderBy{0}",
                direction.ToLower() == "asc" ? "" : "descending");

            ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");

            MemberExpression memberAccess = null;
            foreach (var property in sortColumn.Split('.'))
                memberAccess = MemberExpression.Property
                   (memberAccess ?? (parameter as Expression), property);

            LambdaExpression orderByLambda = Expression.Lambda(memberAccess, parameter);

            MethodCallExpression result = Expression.Call(
                      typeof(Queryable),
                      methodName,
                      new[] { query.ElementType, memberAccess.Type },
                      query.Expression,
                      Expression.Quote(orderByLambda));

            return query.Provider.CreateQuery<T>(result);
        }

        /// <summary>
        /// MANH.NGUYEN - 10/08/2011
        /// Mở rộng toán tử Where của LINQ - bổ sung các toán tử mở rộng
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="column"></param>
        /// <param name="value"></param>
        /// <param name="operation"></param>
        /// <returns></returns>
        public static IQueryable<T> Where<T>(this IQueryable<T> query,
            string column, object value, WhereOperation operation)
        {
            if (string.IsNullOrEmpty(column))
                return query;

            var parameter = Expression.Parameter(query.ElementType, "p");

            Expression memberAccess = null;
            foreach (var property in column.Split('.'))
            {   memberAccess = Expression.Property(memberAccess ?? (parameter as Expression), property);
            }

            //change param value type
            //necessary to getting bool from string
            var type = memberAccess.Type;
            if (memberAccess.Type.IsGenericType && memberAccess.Type.GetGenericTypeDefinition() == typeof(Nullable<>))
                type = memberAccess.Type.GetGenericArguments()[0];

            var filter = Expression.Constant(Convert.ChangeType(value, type));

            //switch operation
            Expression condition = null;
            LambdaExpression lambda = null;

            //MANH.NGUYEN - 10/08/2011
            //Xử lý các kiểu dữ liệu dạng nullable (double) 
            if (IsNullableType(memberAccess.Type))
            {
                memberAccess = Expression.Convert(memberAccess, filter.Type);
            }

            switch (operation)
            {
                //có chứa từ
                case WhereOperation.Equal:
                    condition = Expression.Equal(memberAccess, filter);
                    lambda = Expression.Lambda(condition, parameter);
                    break;
                //không chứa
                case WhereOperation.NotEqual:
                    condition = Expression.NotEqual(memberAccess, filter);
                    lambda = Expression.Lambda(condition, parameter);
                    break;
                //tìm kiếm chính xác
                case WhereOperation.Contains:
                    condition = Expression.Call(memberAccess,
                        typeof(string).GetMethod("Contains"),
                        Expression.Constant(value));
                    lambda = Expression.Lambda(condition, parameter);
                    break;
                //lớn hơn
                case WhereOperation.Greater:
                    condition = Expression.GreaterThan(memberAccess, filter);
                    lambda = Expression.Lambda(condition, parameter);    
                    break;
                //lớn hơn hoặc bằng
                case WhereOperation.GreaterOrEqual:
                    condition = Expression.GreaterThanOrEqual(memberAccess, filter);
                    lambda = Expression.Lambda(condition, parameter);
                    break;
                //nhỏ hơn
                case WhereOperation.Less:
                    condition = Expression.LessThan(memberAccess, filter);
                    lambda = Expression.Lambda(condition, parameter);
                    break;
                //nhỏ hơn hoặc bằng
                case WhereOperation.LessEqual:
                    condition = Expression.LessThanOrEqual(memberAccess, filter);
                    lambda = Expression.Lambda(condition, parameter);
                    break;
            }

            var result = Expression.Call(
                   typeof(Queryable), "Where",
                   new[] { query.ElementType },
                   query.Expression,
                   lambda);

            return query.Provider.CreateQuery<T>(result);
        }

        /// <summary>
        /// Kiểm tra kiểu dữ liệu loại Nullable 
        /// </summary>
        /// <param name="type">kiểu dữ liệu</param>
        private static bool IsNullableType(this Type type)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        }
    }
}
